package net.cl.tools.calculator;

import net.cl.utils.StringUtil;

/**
 * 计算器
 * 
 * @author caol64@gmail.com
 * 
 */
public class Calculator {

	private static final String ALLOW_SYMBOL = ".()0123456789+-*/";// 公式里允许出现的符号
	public static final int ERROR_EXPRESSION = 1;
	public static final int UNMATCH_BRACKETS = 2;
	public static final int ZERO_DIVISOR = 3;

	public Calculator() {
	}

	public String calculate(String expression) throws CalculatorException {
		verify(expression);
		String result = calculateX(expression);
		if (result.endsWith(".0"))
			result = result.substring(0, result.length() - 2);
		return result;
	}

	private String calculateX(String expression) throws CalculatorException {
		if (expression.contains("(")) {
			int start = expression.lastIndexOf("(");
			int end = expression.indexOf(")");
			String str = wordX(expression.substring(start + 1, end) + "=");
			expression = StringUtil.replace(expression, start + 1, end + 1, str);
			return calculateX(expression);
		} else
			return wordX(expression + "=");
	}

	/**
	 * 验证输入公式的合法性
	 * 
	 * @throws CalculatorException
	 */
	private boolean verify(String expression) throws CalculatorException {
		int count = 0;
		for (int i = 0; i < expression.length(); i++) {
			char ch = expression.charAt(i);
			if (ALLOW_SYMBOL.indexOf(ch) < 0) {
				throw new CalculatorException(ERROR_EXPRESSION);
			}
			if (i == 0 || i == expression.length() - 1) {
				if (isOperator(ch) || isPoint(ch))
					throw new CalculatorException(ERROR_EXPRESSION);
			}
			if (isOperator(ch) && isOperator(expression.charAt(i + 1)))
				throw new CalculatorException(ERROR_EXPRESSION);
			if (isPoint(ch) && isPoint(expression.charAt(i + 1)))
				throw new CalculatorException(ERROR_EXPRESSION);
			if (ch == '(')
				count++;
			else if (ch == ')')
				count--;
			if (count < 0)
				throw new CalculatorException(ERROR_EXPRESSION);
		}
		if (count != 0)
			throw new CalculatorException(UNMATCH_BRACKETS);
		return true;
	}

	private boolean isOperator(char c) {
		return c == '+' || c == '-' || c == '/' || c == '*';
	}

	private boolean isPoint(char c) {
		return c == '.';
	}

	/**
	 * 两个数的计算
	 * 
	 * @throws CalculatorException
	 */
	private String numX(String num1, String num2, char p) throws CalculatorException {
		float n1 = Float.parseFloat(num1);
		float n2 = Float.parseFloat(num2);
		float result;
		if (p == '+')
			result = n1 + n2;
		else if (p == '-')
			result = n1 - n2;
		else if (p == '*')
			result = n1 * n2;
		else if (p == '/') {
			if (n2 == 0)
				throw new CalculatorException(ZERO_DIVISOR);
			else
				result = n1 / n2;
		} else
			throw new CalculatorException(ERROR_EXPRESSION);
		return new Float(result).toString();
	}

	/**
	 * 没有括号的复杂运算，如1+2*3+4
	 * 
	 * @throws CalculatorException
	 */
	private String wordX(String expression) throws CalculatorException {
		char p = '\0';
		String s1 = "", s2 = "";
		String str;
		int start = 0;
		boolean flag = expression.contains("*") || expression.contains("/"); // 是否有乘除运算
		if (!expression.contains("+") && !expression.contains("-"))
			flag = false;
		for (int i = 0; i < expression.length(); i++) {
			char ch = expression.charAt(i);
			if (flag) {
				if (isOperator(ch) || ch == '=') {
					if (p == '*' || p == '/') {
						str = numX(s1, s2, p);
						expression = StringUtil.replace(expression, start + 1, i, str);
						return wordX(expression);
					}
					s1 = s2;
					s2 = "";
					p = ch;
					if (ch == '+' || ch == '-')
						start = i + 1;
				} else {
					s2 += ch;
				}
			} else {
				if (isOperator(ch)) {
					if (p != '\0') {
						str = numX(s1, s2, p);
						expression = StringUtil.replace(expression, 1, i, str);
						return wordX(expression);
					}
					s1 = s2;
					s2 = "";
					p = ch;
				} else if (ch == '=') {
					return numX(s1, s2, p);
				} else {
					s2 += ch;
				}
			}
		}
		return expression;
	}

	public static void main(String[] args) {
		Calculator c = new Calculator();
		System.out.println(c.calculate("1*2*3+4/2"));
	}

}
